# TODO Python中关于函数的使用
# 在编程中，函数是一段可重用的代码块，用于执行特定的任务。它可以接受输入参数（可选），并可以返回一个值（也可选）。函数使代码更加模块化、可维护和可复用。
# 可以理解为 给定输入会执行函数内定义的逻辑,会给你一个输出,通过函数省略掉重复代码逻辑的过程.

# 1、什么是函数 ?
# 函数是组织好的、可重复使用的、用来实现单一或相关联功能的代码段。函数能提高应用的模块性，和代码的重复利用率。并且我们已经使用了 Python 提供了许多内建函数，如 print()，len() 等，并且可以自己创建函数，这被叫做用户定义函数。

# 2、函数的定义
# 你可以定义一个由自己想要功能的函数，以下是简单的规则：
# 函数代码块以 def 关键词开头，后接函数标识符名称和圆括号()。
# 任何传入参数和自变量必须放在圆括号中间，圆括号之间可以用于定义参数。
# 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
# 函数内容以冒号起始，并且缩进。
# return [表达式] 结束函数，选择性地返回一个值给调用方。不带表达式的 return 相当于返回 None。

# 注意事项：函数的名称应该能体现函数的功能，通常我们使用动词或动词短语来命名函数。函数的参数应当是一个名词或名词短语。
# 以下是一个函数的定义示例：
def greet(name):
    """这个函数对作为参数传入的人表示欢迎"""
    print("Hello, " + name + ". Good morning!")


#   函数调用
greet('Ashe')  # Hello, Ashe. Good morning!


# 函数的返回值类型
# Python 函数的返回值是通过 return 语句实现的。return 语句可以携带一个值，这个值会被作为函数的返回值。如果没有 return 语句，函数将默认返回 None。
# 注意事项：返回值应当是函数主要的操作结果，如果函数需要提供多个返回值，一般使用元组或字典来组织。
# 以下是一个使用 return 语句返回值的函数：
def sum_two_numbers(a, b):
    """Return the sum of two numbers."""
    return a + b


print(sum_two_numbers(3, 4))  # 输出：7


# 通过上面的返回值类型的例子中,我们发现,python 不需要我们手动指定返回值类型.
# 在Python中，函数的返回值类型不需要显式声明。函数可以返回任何类型的数据，也可以没有返回值（即返回None）。实际上，所有Python函数都有返回值，如果你没有显式指定返回值，那么Python会默认返回None。
# 例如，下面的函数没有显式返回值：
def greet(name):
    print("Hello, " + name)


result = greet("Alice")
print(result)  # 输出：None


# 4、函数参数
# 函数可以接受输入参数，这使得函数更加灵活和通用。你可以在函数定义时指定参数的名称，并在调用函数时传递相应的值。下面是一个接受参数的函数示例：
def greet(name):
    print("Hello, " + name + "!")


greet("Alice")  # 输出：Hello, Alice!
greet("Bob")  # 输出：Hello, Bob!


# 默认参数
# 默认参数是在函数定义的时候赋予的默认值。如果在调用函数的时候没有提供相应的参数值，那么函数就会使用这个默认值。
# 注意事项：当使用参数名称传参时，参数的顺序无关紧要。但如果要混合使用顺序传参和参数名称传参，那么顺序传参的参数必须在前面。
# 下面是一个使用默认参数的例子：

def greet(name, message="Good morning!"):
    """
    This function greets to the person with the provided message.
    If message is not provided, it defaults to "Good morning!"
    """
    print(f"Hello, {name}. {message}")


greet("Paul")  # 输出：Hello, Paul. Good morning!
greet("Paul", "How are you?")  # 输出：Hello, Paul. How are you?


# 可变长参数列表
# 在 Python 中，有时候我们可能想要在函数中处理一个可变数量的参数，这时我们可以使用可变长参数列表。你可以在参数名前加一个 * 符号来定义一个可变长参数列表。
# 下面是一个使用可变长参数列表的例子：
def print_args(*args):
    """This function prints all the arguments passed to it."""
    print(args)


# 在这个例子中，函数 print_args 接收任意数量的参数，并将它们打包成一个元组。
# 注意事项：可变长参数列表可以和其他参数一起使用，但必须放在所有非可变长参数后面。你只能在函数定义中使用一个 * 参数。
print_args(1, 2, 3, "Hello", [1, 2, 3])


# 输出：(1, 2, 3, 'Hello', [1, 2, 3])


# 可变长键值参数
# 类似于可变长参数列表，你也可以在函数中处理一个可变数量的键值对参数，这时可以使用可变长键值参数。你可以在参数名前加一个 ** 符号来定义一个可变长键值参数。
# 在这个例子中，函数 print_kwargs 接收任意数量的键值对参数，并将它们打包成一个字典。
# 注意事项：可变长键值参数可以和其他参数一起使用，但必须放在所有非可变长参数后面。你只能在函数定义中使用一个 ** 参数。
# 下面是一个使用可变长键值参数的例子：
def print_kwargs(**kwargs):
    """This function prints all the keyword arguments passed to it."""
    print(kwargs)


print_kwargs(name="Paul", message="Good morning!")


# 输出：{'name': 'Paul', 'message': 'Good morning!'}

# 另外，Python的动态类型特性使得函数可以返回不同类型的数据，这取决于函数的具体实现和输入。例如，下面的函数根据输入参数的类型返回不同类型的数据：
def func(x):
    if isinstance(x, int):
        return x + 1
    elif isinstance(x, str):
        return x + "1"
    else:
        return None


print(func('Ashe'))  # Ashe1
print(func(22))  # 23
print(func(22.88))  # None


# 关于参数顺序的说明
# 在Python中，函数参数的书写顺序确实有一定的规则。一般来说，参数的顺序应该是：必须参数（普通参数）、默认参数、可变长参数和键值参数。这是因为解析参数值的方式是从左到右，如果不遵循这个顺序，Python可能无法正确地将输入参数映射到函数参数。
# 以下是一个正确参数顺序的例子：
def example_func(param1, param2='任何人都可以有很多老婆.....', *args, **kwargs):
    print(param1)
    print(param2)
    for i in args:
        print(f'{param1}的老婆: {i}')
    for key, value in kwargs:
        print(key, value)


example_func('tiancy', '只是我才能拥有很多老婆', ['李 沁', '金晨', '李一桐'], {'key1': 'value1'})


# 下面这种语法会报错
# def bad_example_func(param1='default_value', param2, *args, **kwargs):
#     pass

# 总的来说，为了避免错误和混淆，建议总是遵循参数的正确顺序：必须参数、默认参数、可变长参数和键值参数。


# 关于函数调用时传递参数的写法说明: 顺序传参和参数名传参

# 顺序传参：参数的顺序与定义函数时的参数顺序相同。在以下的例子中，a、b和c就是通过顺序传参赋值的。
def my_func(a, b, c):
    print(a, b, c)


my_func(1, 2, 3)  # 输出：1 2 3


# 参数名称传参：在调用函数时，通过参数名=值的形式为函数的参数赋值。这种方式不需要考虑参数的顺序，只需要确保所有的必需参数都被赋值。
# 这里需要注意的是，使用参数名称传参可以忽略参数的顺序，但是必须确保参数名正确，且所有的必需参数都被赋值。当然，也可以混合使用顺序传参和参数名称传参。
def my_func(a, b, c):
    print(a, b, c)


my_func(c=3, a=1, b=2)  # 输出：1 2 3
# 这两种参数传递方式可以根据具体情况选择使用，一般情况下，如果函数的参数较少，并且含义明显，我们可以使用顺序传参。如果函数的参数较多或者参数的含义不是很明显，我们建议使用参数名称传参，因为这样可以提高代码的可读性。


# 匿名函数（Lambda函数）
# 在Python中，你可以使用lambda关键字来创建匿名函数，即没有名称的函数。匿名函数主要用于实现简单的、单行的函数功能。
# 匿名函数的语法格式如下：
# lambda arguments: expression
# 这里，arguments表示函数的参数，可以有多个，用逗号隔开。expression是一个关于参数的表达式，该表达式的值会被作为函数的返回值。
# 举个例子：
# 定义一个匿名函数，该函数接受两个参数，并返回它们的和
sum_func = lambda x, y: x + y
print(sum_func(1, 2))  # 输出：3

# 这里，我们创建了一个匿名函数，该函数接受两个参数x和y，并返回它们的和。然后，我们将这个匿名函数赋值给变量sum_func，这样我们就可以通过sum_func来调用这个匿名函数了。

# 注意事项：
# 由于lambda表达式只能包含一个表达式，所以它的功能相对有限。如果函数的功能较为复杂，你还是应该使用def来定义函数。
# lambda创建的是一个函数对象，可以将其赋值给一个变量，也可以直接作为函数的参数使用。
# 与def定义的函数不同，lambda函数没有函数名，这也是它被称为“匿名函数”的原因。虽然在上面的例子中我们给这个匿名函数赋了一个名字sum_func，但这其实是给这个函数对象的引用赋了一个名字，而不是给这个函数赋了一个名字。
# lambda函数的主体只是一个表达式，而不是一个代码块。这意味着lambda函数的主体不能包含复杂的逻辑，不能包含条件分支、循环等语句。




